//
// Created by lymk on 2022/2/26.
//

#include "WindowsApplication.h"

//
// Created by lymk on 2021/9/23.
//

#include "iostream"
#include <utility>
#include "tchar.h"
#include "Buffer.h"
bool WindowsApplication::bShouldClose = false;
WindowsApplication::WindowsApplication() {
    appName = "Default name";
    screenHeight = 600;
    screenWidth = 600;
}

WindowsApplication::WindowsApplication(std::string name, uint32_t height, uint32_t width) {
    appName = std::move(name);
    screenHeight = height;
    screenWidth = width;
}

int WindowsApplication::Initialize() {
    inputManager = new InputManager();

    // get the HINSTANCE of the Console Program
    HINSTANCE hInstance = GetModuleHandle(NULL);

    // the handle for the window, filled by a function
    HWND hWnd;
    // this struct holds information for the window class
    WNDCLASSEX wc;

    // clear out the window class for use
    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    // fill in the struct with the needed information
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = _T("GameEngineFromScratch");

    // register the window class
    RegisterClassEx(&wc);

    // create the window and use the result as the handle
    hWnd = CreateWindowEx(0,
                          _T("GameEngineFromScratch"),   // name of the window class
                          appName.data(),                          // title of the window
                          WS_OVERLAPPEDWINDOW,              // window style
                          CW_USEDEFAULT,                    // x-position of the window
                          CW_USEDEFAULT,                    // y-position of the window
                          screenWidth,                      // width of the window
                          screenHeight,                     // height of the window
                          NULL,                             // we have no parent window, NULL
                          NULL,                             // we aren't using menus, NULL
                          hInstance,                        // application handle
                          this);                            // used with multiple windows, NULL

    // display the window on the screen
    ShowWindow(hWnd, SW_SHOW);

    m_hWnd = hWnd;

    m_hDc = GetDC(m_hWnd);
    HBITMAP tmp_Map = CreateCompatibleBitmap(m_hDc,screenWidth,screenHeight);
    if(tmp_Map == nullptr){
        std::cout<<"位图创建失败失败1"<<std::endl;
        return -1;
    }
    m_bufDc = CreateCompatibleDC(m_hDc);
    if(m_bufDc == nullptr){
        std::cout<<" 失败2"<<std::endl;
    }
    DeleteObject(SelectObject(m_bufDc,tmp_Map));
    renderPipeline = new RenderPipeline();
    renderPipeline->Initialize(screenWidth,screenHeight);
    return 0;
}

void WindowsApplication::Finalize() {

}

void WindowsApplication::Tick(GameTimer* mTimer) {
// this struct holds Windows event messages
    MSG msg;

    // we use PeekMessage instead of GetMessage here
    // because we should not block the thread at anywhere
    // except the engine execution driver module
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        // translate keystroke messages into the right format
        TranslateMessage(&msg);

        // send the message to the WindowProc function
        DispatchMessage(&msg);
    }
    Buffer* buffer = renderPipeline->GetRenderBuffer();
    BITMAPINFO bmInfo;
    memset(&bmInfo.bmiHeader, 0, sizeof(BITMAPINFOHEADER));
    bmInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmInfo.bmiHeader.biWidth = buffer->getWidth();
    bmInfo.bmiHeader.biHeight = -1*buffer->getHeight();
    bmInfo.bmiHeader.biPlanes = 1;
    bmInfo.bmiHeader.biBitCount = buffer->getNChannels() * 8;//32位位图支持透明度
    SetDIBitsToDevice(m_bufDc, 0, 0, buffer->getWidth(), buffer->getHeight(), 0, 0, 0, buffer->getHeight(), buffer->getData(), &bmInfo, 0);
    BitBlt(m_hDc,0,0,screenWidth,screenHeight,m_bufDc,0,0,SRCCOPY);
    //renderPipeline->Tick(mTimer);
}

LRESULT WindowsApplication::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    WindowsApplication* pThis;
    if (message == WM_NCCREATE) {
        pThis = static_cast<WindowsApplication*>(
                reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);

        SetLastError(0);
        if (!SetWindowLongPtr(hWnd, GWLP_USERDATA,
                              reinterpret_cast<LONG_PTR>(pThis))) {
            if (GetLastError() != 0) return FALSE;
        }
    } else {
        pThis = reinterpret_cast<WindowsApplication*>(
                GetWindowLongPtr(hWnd, GWLP_USERDATA));
    }

    // sort through and find what code to run for the message given
    switch(message)
    {
        case WM_PAINT:
            // we will replace this part with Rendering Module
        {

        } break;
        case WM_KEYUP: {
            pThis->inputManager->SetKeyBoardState(wParam,false);
        } break;
        case WM_KEYDOWN: {
            pThis->inputManager->SetKeyBoardState(wParam,true);
        } break;
        case WM_LBUTTONUP: {
            int tmpX = GET_X_LPARAM(lParam);
            int tmpY = GET_Y_LPARAM(lParam);
            pThis->inputManager->UpdateMousePosition(tmpX,tmpY);
            pThis->inputManager->SetMouseState(0, false);
        }break;
        case WM_LBUTTONDOWN: {
            int tmpX = GET_X_LPARAM(lParam);
            int tmpY = GET_Y_LPARAM(lParam);
            pThis->inputManager->UpdateMousePosition(tmpX,tmpY);
            pThis->inputManager->SetMouseState(0, true);
        }break;
        case WM_RBUTTONDOWN: {
            int tmpX = GET_X_LPARAM(lParam);
            int tmpY = GET_Y_LPARAM(lParam);
            pThis->inputManager->UpdateMousePosition(tmpX,tmpY);
            pThis->inputManager->SetMouseState(2, true);
        }break;
        case WM_RBUTTONUP: {
            int tmpX = GET_X_LPARAM(lParam);
            int tmpY = GET_Y_LPARAM(lParam);
            pThis->inputManager->UpdateMousePosition(tmpX,tmpY);
            pThis->inputManager->SetMouseState(2, false);
        }break;
        case WM_MOUSEMOVE: {
            int tmpX = GET_X_LPARAM(lParam);
            int tmpY = GET_Y_LPARAM(lParam);
            pThis->inputManager->UpdateMousePosition(tmpX,tmpY);
        }break;
            // this message is read when the window is closed
        case WM_DESTROY:
        {
            WindowsApplication::bShouldClose = true;
            PostQuitMessage(0);
            return 0;
        }
    }

    // Handle any messages the switch statement didn't
    return DefWindowProc (hWnd, message, wParam, lParam);
}

uint32_t WindowsApplication::getScreenWidth() const {
    return screenWidth;
}

void WindowsApplication::setScreenWidth(uint32_t screenWidth) {
    WindowsApplication::screenWidth = screenWidth;
}

uint32_t WindowsApplication::getScreenHeight() const {
    return screenHeight;
}

void WindowsApplication::setScreenHeight(uint32_t screenHeight) {
    WindowsApplication::screenHeight = screenHeight;
}

